// TouchFlo Detacher
// Copyright (C) 2009, Avian Waves Software
// SEE LICENSE.TXT for software licensing information regarding original TouchFlo Detacher source code and binaries
// SEE OpenNetCF_LICENSE.TXT for licensing information regarding OpenNetCF binaries

#include <Shellapi.h>
#include <Tlhelp32.h>
#pragma comment (lib,"toolhelp.lib") 

#define TH32CS_SNAPNOHEAPS 0x40000000 

extern HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID);
extern BOOL WINAPI CloseToolhelp32Snapshot(HANDLE hSnapshot);

DWORD GetPIDByProcName(wchar_t *procName)
{
  DWORD pid = 0;
  PROCESSENTRY32 pe32 = {0}; 

  HANDLE thh = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPNOHEAPS, 0);

  if (thh != (HANDLE) -1)
  {
    pe32.dwSize = sizeof(PROCESSENTRY32);
    if (Process32First(thh, &pe32)) 
    { 
      do 
      { 
        if (!_wcsicmp(pe32.szExeFile,procName)) 
        { 
          pid = pe32.th32ProcessID;
          break;
        } 
      } 
      while (Process32Next(thh, &pe32)); 
    }

    CloseToolhelp32Snapshot(thh);
  }
  
  return pid;
}

BOOL GetRegSZ(wchar_t *value, wchar_t *data, DWORD maxlength)
{
  HKEY hkey;
  DWORD length = maxlength * sizeof(wchar_t);
  BOOL ret = FALSE;

  DWORD type = REG_SZ;
  DWORD disp;

  if (RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Avian Waves\\TouchFlo Detacher", 0, 0, 0, 0, 0, &hkey, &disp) == ERROR_SUCCESS)
  {
    if (RegQueryValueEx(hkey, value, 0, &type, (LPBYTE)data, &length) == ERROR_SUCCESS)
    {
      ret = TRUE;
    }

    RegCloseKey(hkey);
  }

  return ret;
}

BOOL GetRegDW(wchar_t *value, DWORD *data)
{
  HKEY hkey;
  DWORD length = sizeof(DWORD);
  BOOL ret = FALSE;

  DWORD type = REG_DWORD;
  DWORD disp;

  if (RegCreateKeyEx(HKEY_CURRENT_USER, L"Software\\Avian Waves\\TouchFlo Detacher", 0, 0, 0, 0, 0, &hkey, &disp) == ERROR_SUCCESS)
  {
    if (RegQueryValueEx(hkey, value, 0, &type, (LPBYTE)data, &length) == ERROR_SUCCESS)
    {
      ret = TRUE;
    }

    RegCloseKey(hkey);
  }

  return ret;
}

DWORD FindManilaPID()
{
  wchar_t procname[201];

  if (GetRegSZ(L"TouchFloEXEOnly",procname,200))
  {
    return GetPIDByProcName(procname);
  }
 
  return 0;
}

HWND FindFirstManilaHwndByClass(wchar_t *mclass)
{
  DWORD mpid = FindManilaPID();
  HWND hwnd;

  if (mpid > 0)
  {
    hwnd = GetForegroundWindow();
    hwnd = GetWindow(hwnd, GW_HWNDFIRST);

    while (hwnd != NULL)
    {
      wchar_t pclass[201] = {0};
      GetClassName(hwnd, pclass, 200);
      
      if (!_wcsicmp(mclass, pclass)) 
      { 
        DWORD pid = 0;
        GetWindowThreadProcessId(hwnd, &pid);

        if (pid == mpid)
        {
          return hwnd;
        }
      } 

      hwnd = GetWindow(hwnd, GW_HWNDNEXT);
    }
  }

  return NULL;
}

int FindAllManilaHwndByClass(wchar_t *mclass, HWND *hwnds, int numhws)
{
  DWORD mpid = FindManilaPID();
  HWND hwnd;
  int count = 0;

  if (mpid > 0 && count < numhws)
  {
    hwnd = GetForegroundWindow();
    hwnd = GetWindow(hwnd, GW_HWNDFIRST);

    while (hwnd != NULL)
    {
      wchar_t pclass[201] = {0};
      GetClassName(hwnd, pclass, 200);
      
      if (!_wcsicmp(mclass, pclass)) 
      { 
        DWORD pid = 0;
        GetWindowThreadProcessId(hwnd, &pid);

        if (pid == mpid)
        {
          hwnds[count] = hwnd;
          count ++;
        }
      } 

      hwnd = GetWindow(hwnd, GW_HWNDNEXT);
    }
  }

  return count;
}

//HWND FindNextManilaHwndByClass(wchar_t *mclass, HWND prevHwnd)
//{
//  DWORD mpid = FindManilaPID();
//  HWND hwnd;
//
//  BOOL found = FALSE;
//
//  if (mpid > 0)
//  {
//    hwnd = GetForegroundWindow();
//    hwnd = GetWindow(hwnd, GW_HWNDFIRST);
//
//    while (hwnd != NULL)
//    {
//      wchar_t pclass[201] = {0};
//      GetClassName(hwnd, pclass, 200);
//      
//      if (!_wcsicmp(mclass, pclass)) 
//      { 
//        DWORD pid = 0;
//        GetWindowThreadProcessId(hwnd, &pid);
//
//        if (pid == mpid)
//        {
//          if (!found)
//          {
//            if (prevHwnd == hwnd)
//            {
//              found = TRUE;
//            }
//          }
//          else
//          {
//            return hwnd;
//          }
//        }
//      } 
//
//      hwnd = GetWindow(hwnd, GW_HWNDNEXT);
//    }
//  }
//
//  return NULL;
//}


/////////////////////////////////

// Show Manila! (or launch it)
void ShowManila()
{
  // Show current desktop
  SetForegroundWindow(GetDesktopWindow());

  // Find Manila's main window
  wchar_t mclass[201];
  if (GetRegSZ(L"TouchFloMainWindowclass",mclass,200))
  {
    HWND hwnd = FindFirstManilaHwndByClass(mclass);

    if (hwnd > 0)
    {
      // Show Manila's main window
      ShowWindow(hwnd, SW_SHOWNORMAL);

      // Make sure Manila is top-most in the z-order
      SetForegroundWindow(hwnd);
      BringWindowToTop(hwnd);

      // Make sure Manila's softkeys are visible
      if (GetRegSZ(L"TouchFloSoftKeyWindowclass",mclass,200))
      {
        HWND hwnds[50];
        int count = FindAllManilaHwndByClass(mclass, hwnds, 50);
        for (int i=0; i<count; i++)
        {
          ShowWindow(hwnds[i], SW_SHOWNORMAL);
        }
      }
    }
    else
    {
      wchar_t path[1001];
      if (GetRegSZ(L"TouchFloFullPath",path,1000))
      {
        SHELLEXECUTEINFO sei;
        sei.lpFile = path;
        sei.cbSize = sizeof(sei);
        sei.nShow = SW_SHOWNORMAL;
        ShellExecuteEx(&sei);
      }

      DWORD launchOther = 0;
      if (GetRegDW(L"LaunchAppAfterTouchFlo",&launchOther))
      {
        if (launchOther)
        {
          DWORD lenwait = 0;
          if (GetRegDW(L"LaunchAppAfterTouchFloDelayInSeconds",&lenwait))
          {
            Sleep(lenwait * 1000);
          }

          if (GetRegSZ(L"LaunchAppAfterTouchFloPath",path,1000))
          {
            SHELLEXECUTEINFO sei;
            sei.lpFile = path;
            sei.cbSize = sizeof(sei);
            sei.nShow = SW_SHOWNORMAL;
            ShellExecuteEx(&sei);
          }
        }
      }
    }
  }
}

// Hide (but do not close) Manila!
void HideManila()
{
  // Show current desktop
  SetForegroundWindow(GetDesktopWindow());

  // Find Manila's main window
  wchar_t mclass[201];
  if (GetRegSZ(L"TouchFloMainWindowclass",mclass,200))
  {
    HWND hwnd = FindFirstManilaHwndByClass(mclass);

    if (hwnd > 0)
    {
      // Hide Manila's softkey panels (all)
      if (GetRegSZ(L"TouchFloSoftKeyWindowclass",mclass,200))
      {
        HWND hwnds[50];
        int count = FindAllManilaHwndByClass(mclass, hwnds, 50);
        for (int i=0; i<count; i++)
        {
          ShowWindow(hwnds[i], SW_HIDE);
        }
      }

      // Hide Manila's main window
      ShowWindow(hwnd, SW_HIDE);
    }
  }

  // Show the Manilaless desktop
  SetForegroundWindow(GetDesktopWindow());
}

// Close Manila!
void CloseManila()
{
  DWORD pid = FindManilaPID();

  if (pid > 0)
  {
    HANDLE proc = OpenProcess(0, FALSE, pid);

    if (proc != NULL)
    {
      TerminateProcess(proc, 0);
    }
  }

  // Show the Manilaless desktop
  SetForegroundWindow(GetDesktopWindow());
}